package com.uuah.server.config.spring;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.beans.factory.xml.XmlReaderContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.uuah.server.util.spring.SpringXMLUtils;
import com.uuah.utils.StringUtils;

/**
 * This parser enables Uuah to parse heirarchical bean structures using spring
 * Namespace handling There are 4 base DefinitionParsers supplied in Uuah that
 * most Parsers will extend from, these are
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午01:25:41
 * @version 1.0.0
 */
public class UuahHierarchicalBeanDefinitionParserDelegate extends
		BeanDefinitionParserDelegate {

	public static final String BEANS = "beans";
	// api!
	public static final String UUAH_REPEAT_PARSE = "com.uuah.server.config.spring.UuahHierarchicalBeanDefinitionParserDelegate.UUAH_REPEAT_PARSE";
	public static final String UUAH_NO_RECURSE = "com.uuah.server.config.spring.UuahHierarchicalBeanDefinitionParserDelegate.UUAH_NO_RECURSE";
	public static final String UUAH_FORCE_RECURSE = "com.uuah.server.config.spring.UuahHierarchicalBeanDefinitionParserDelegate.UUAH_FORCE_RECURSE";
	public static final String UUAH_NO_REGISTRATION = "com.uuah.server.config.spring.UuahHierarchicalBeanDefinitionParserDelegate.UUAH_NO_REGISTRATION";
	public static final String UUAH_POST_CHILDREN = "com.uuah.server.config.spring.UuahHierarchicalBeanDefinitionParserDelegate.UUAH_POST_CHILDREN";
	private DefaultBeanDefinitionDocumentReader spring;

	protected transient final Logger logger = LoggerFactory
			.getLogger(UuahHierarchicalBeanDefinitionParserDelegate.class);

	public UuahHierarchicalBeanDefinitionParserDelegate(
			XmlReaderContext readerContext,
			DefaultBeanDefinitionDocumentReader spring) {
		super(readerContext);
		this.spring = spring;
	}

	public BeanDefinition parseCustomElement(Element element,
			BeanDefinition parent) {
		if (logger.isDebugEnabled()) {
			logger.debug("parsing: " + SpringXMLUtils.elementToString(element));
		}
		if (SpringXMLUtils.isBeansNamespace(element)) {
			return handleSpringElements(element, parent);
		} else {
			String namespaceUri = element.getNamespaceURI();
			NamespaceHandler handler = getReaderContext()
					.getNamespaceHandlerResolver().resolve(namespaceUri);
			if (handler == null) {
				getReaderContext().error(
						"Unable to locate NamespaceHandler for namespace ["
								+ namespaceUri + "]", element);
				return null;
			}

			boolean noRecurse = false;
			boolean forceRecurse = false;
			BeanDefinition finalChild;

			do {
				ParserContext parserContext = new ParserContext(
						getReaderContext(), this, parent);
				finalChild = handler.parse(element, parserContext);
				registerBean(element, finalChild);
				noRecurse = noRecurse || testFlag(finalChild, UUAH_NO_RECURSE);
				forceRecurse = forceRecurse
						|| testFlag(finalChild, UUAH_FORCE_RECURSE);
			} while (null != finalChild
					&& testFlag(finalChild, UUAH_REPEAT_PARSE));

			boolean isRecurse;
			if (noRecurse) {
				// no recursion takes precedence, as recursion is set by default
				isRecurse = false;
			} else {
				if (forceRecurse) {
					isRecurse = true;
				} else {
					// default behaviour if no control specified
					isRecurse = SpringXMLUtils.isUuahNamespace(element);
				}
			}

			if (isRecurse) {
				NodeList list = element.getChildNodes();
				for (int i = 0; i < list.getLength(); i++) {
					if (list.item(i) instanceof Element) {
						parseCustomElement((Element) list.item(i), finalChild);
					}
				}
			}

			// If a parser requests post-processing we call again after children
			// called

			if (testFlag(finalChild, UUAH_POST_CHILDREN)) {
				ParserContext parserContext = new ParserContext(
						getReaderContext(), this, parent);
				finalChild = handler.parse(element, parserContext);
			}

			return finalChild;
		}
	}

	protected BeanDefinition handleSpringElements(Element element,
			BeanDefinition parent) {

		// these are only called if they are at a "top level" - if they are
		// nested inside
		// other spring elements then spring will handle them itself

		if (SpringXMLUtils.isLocalName(element, BEANS)) {
			// the delegate doesn't support the full spring schema, but it seems
			// that
			// we can invoke the DefaultBeanDefinitionDocumentReader via
			// registerBeanDefinitions
			// but we need to create a new DOM document from the element first
			try {
				Document doc = DocumentBuilderFactory.newInstance()
						.newDocumentBuilder().newDocument();
				doc.appendChild(doc.importNode(element, true));
				spring.registerBeanDefinitions(doc, getReaderContext());
			} catch (ParserConfigurationException e) {
				throw new RuntimeException(e);
			}
			return parent;
		}

		else if (SpringXMLUtils.isLocalName(element, PROPERTY_ELEMENT)) {
			parsePropertyElement(element, parent);
			return parent;
		}

		else if (SpringXMLUtils.isLocalName(element, BEAN_ELEMENT)) {
			BeanDefinitionHolder holder = parseBeanDefinitionElement(element,
					parent);
			registerBeanDefinitionHolder(holder);
			return holder.getBeanDefinition();
		} else {
			throw new IllegalStateException("Unexpected Spring element: "
					+ SpringXMLUtils.elementToString(element));
		}
	}

	protected void registerBean(Element ele, BeanDefinition bd) {
		if (bd == null) {
			return;
		}
		if (!testFlag(bd, UUAH_NO_REGISTRATION)) {
			String name = generateChildBeanName(ele);
			if (logger.isDebugEnabled()) {
				logger.debug("register " + name + ": " + bd.getBeanClassName());
			}
			registerBeanDefinitionHolder(new BeanDefinitionHolder(bd, name));
		}
	}

	protected void registerBeanDefinitionHolder(BeanDefinitionHolder bdHolder) {
		BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
				getReaderContext().getRegistry());
		getReaderContext().fireComponentRegistered(
				new BeanComponentDefinition(bdHolder));
	}

	protected String generateChildBeanName(Element e) {
		String id = SpringXMLUtils.getNameOrId(e);
		if (StringUtils.isBlank(id)) {
			String parentId = SpringXMLUtils.getNameOrId((Element) e
					.getParentNode());
			return "." + parentId + ":" + e.getLocalName();
		} else {
			return id;
		}
	}

	public static void setFlag(BeanDefinition bean, String flag) {
		bean.setAttribute(flag, Boolean.TRUE);
	}

	public static boolean testFlag(BeanDefinition bean, String flag) {
		return null != bean && bean.hasAttribute(flag)
				&& bean.getAttribute(flag) instanceof Boolean
				&& ((Boolean) bean.getAttribute(flag)).booleanValue();
	}

}
